﻿using IT.CMS.Core;
using IT.CMS.Core.Data;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using MongoDB.Driver.Builders;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;

namespace IT.CMS.Data
{

    /// <summary>
    /// class that spawns MonogDatabase and MongoCollection objects
    /// </summary>



    public partial class MongoRepository<T> : IRepository<T> where T : BaseEntity
    {
        private string _mongoUrlString;
        private MongoCollection<T> _collection;


        private MongoDatabase GetDatabase()
        {
                var url = new MongoUrl(_mongoUrlString);
                var server = MongoServer.Create(url);
                return server.GetDatabase(url.DatabaseName);

            throw new InvalidOperationException("Need a connection string in your app/web.config file");
        }

        private MongoCollection<T> GetCollection<T>() where T : BaseEntity
        {
            string collectionName = string.Empty;

            if (BsonClassMap.IsClassMapRegistered(typeof(T)))
            {
                collectionName = BsonClassMap.GetRegisteredClassMaps().Where(cm => cm.ClassType == typeof(T)).FirstOrDefault().Discriminator;
            }
            else
            {
                collectionName = typeof(T).FullName;
            }
            return GetDatabase().GetCollection<T>(collectionName);
        }


        public MongoRepository()
        {
            var dataSettingsManager = new DataSettingsManager();
            var dataProviderSettings = dataSettingsManager.LoadSettings();
            this._mongoUrlString = dataProviderSettings.DataConnectionString;
            this._collection = GetCollection<T>();
        }


        public T GetById(object id)
        {
            MongoCollection coll = this.GetCollection<T>();
            
            return coll.FindOneByIdAs<T>((int)id);
        }

        public void Insert(T entity)
        {
            Type type = typeof(T);
            PropertyInfo id_propertyInfo = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.Name == "Id" && p.PropertyType.FullName == "System.Int32").FirstOrDefault();
            if (id_propertyInfo != null)
            {
                entity.GetType().GetProperty(id_propertyInfo.Name).SetValue(entity, GetNextSequence(GetCollection<T>() + "_" + id_propertyInfo.Name));
            }
            this._collection.Insert<T>(entity);
        }

        public void Update(T entity)
        {

            Type type = typeof(T);
            PropertyInfo id_propertyInfo = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.Name == "Id" && p.PropertyType.FullName == "System.Int32").FirstOrDefault();
            if (id_propertyInfo != null && (Int32)entity.GetType().GetProperty(id_propertyInfo.Name).GetValue(entity) == 0)
            {
                entity.GetType().GetProperty(id_propertyInfo.Name).SetValue(entity, GetNextSequence(GetCollection<T>() + "_" + id_propertyInfo.Name));
            }
            this._collection.Save<T>(entity);
        }

        public void Delete(string id)
        {
            this._collection.Remove(Query.EQ("_id", new ObjectId(id)));
        }

        public void Delete(T entity)
        {
            this.Delete(entity.Id.ToString());
        }

        public IQueryable<T> Table
        {
            get
            {
                return this.GetCollection<T>().AsQueryable<T>();
            }
        }



        public int GetNextSequence(string name)
        {
            var collection = GetDatabase().GetCollection("_sequence");
            IMongoQuery query = Query.EQ("_id", name);
            var sortBy = SortBy.Descending("_id");
            IMongoUpdate update = MongoDB.Driver.Builders.Update.Inc("seq", 1);
            var result = collection.FindAndModify(query, sortBy, update, true, true);
            return int.Parse(result.ModifiedDocument[1].ToString());
        }

    }



}
